package com.common.http.basecore.manager;


import android.content.Context;
import android.os.Environment;

import com.common.http.basecore.bean.response.AbsBaseReponse;
import com.common.http.basecore.bean.response.HttpBaseResponseUtils;
import com.common.http.basecore.builder.GetBuilder;
import com.common.http.basecore.builder.HeadBuilder;
import com.common.http.basecore.builder.OtherRequestBuilder;
import com.common.http.basecore.builder.PostFileBuilder;
import com.common.http.basecore.builder.PostFormBuilder;
import com.common.http.basecore.builder.PostStringBuilder;
import com.common.http.basecore.callback.Callback;
import com.common.http.basecore.https.HttpsUtils;
import com.common.http.basecore.request.RequestCall;
import com.common.http.basecore.utils.HttpNetworkUtils;
import com.common.http.basecore.utils.LoggingInterceptor;
import com.common.http.basecore.utils.Platform;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Response;

/**
 */
public class OkHttpClientManager {
    public static final long DEFAULT_MILLISECONDS = 10_000L;
    private static final int DEFAULT_TIMEOUT = 10;
    private volatile static OkHttpClientManager mInstance;
    private OkHttpClient mOkHttpClient;
    private Platform mPlatform;
    private OkHttpClient.Builder builder;
    private Context mContext;

    public OkHttpClientManager (OkHttpClient okHttpClient, Context mContext) {
        this.mContext = mContext;
        if (okHttpClient == null) {
            builder = new OkHttpClient.Builder();
//            builder.addNetworkInterceptor(new StethoInterceptor());
            builder.addInterceptor(new LoggingInterceptor());
            builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
            builder.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);//设置读取超时时间
            builder.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);//设置写入超时时间
            builder.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify (String hostname, SSLSession session) {
                    return true;
                }
            });
            HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
            builder.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
            int cacheSize = 10 * 1024 * 1024; // 10 MiB
            File httpCacheDirectory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/v1video", "cacheData");
            Cache cache = new Cache(httpCacheDirectory, cacheSize);
            builder.cache(cache);
            mOkHttpClient = builder.build();
        } else {
            mOkHttpClient = okHttpClient;
        }
        mPlatform = Platform.get();
    }

    public OkHttpClientManager (OkHttpClient okHttpClient) {
        if (okHttpClient == null) {
            builder = new OkHttpClient.Builder();
//            builder.addNetworkInterceptor(new StethoInterceptor());
            builder.addInterceptor(new LoggingInterceptor());
            builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
            builder.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);//设置读取超时时间
            builder.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);//设置写入超时时间
            builder.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify (String hostname, SSLSession session) {
                    return true;
                }
            });
            HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
            builder.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
            int cacheSize = 10 * 1024 * 1024; // 10 MiB
            File httpCacheDirectory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/v1video", "cacheData");
            Cache cache = new Cache(httpCacheDirectory, cacheSize);
            builder.cache(cache);
            mOkHttpClient = builder.build();
        } else {
            mOkHttpClient = okHttpClient;
        }
        mPlatform = Platform.get();
    }


    public static OkHttpClientManager initClient (OkHttpClient okHttpClient, Context mContext) {
        if (mInstance == null) {
            synchronized (OkHttpClientManager.class) {
                if (mInstance == null) {
                    mInstance = new OkHttpClientManager(okHttpClient, mContext);
                }
            }
        }
        return mInstance;
    }

    public static OkHttpClientManager initClient (OkHttpClient okHttpClient) {
        if (mInstance == null) {
            synchronized (OkHttpClientManager.class) {
                if (mInstance == null) {
                    mInstance = new OkHttpClientManager(okHttpClient);
                }
            }
        }
        return mInstance;
    }

    public static OkHttpClientManager getInstance () {
        return initClient(null);
    }


    public Executor getDelivery () {
        return mPlatform.defaultCallbackExecutor();
    }

    public OkHttpClient getOkHttpClient () {
        return mOkHttpClient;
    }

    public static GetBuilder get () {
        return new GetBuilder();
    }

    public static PostStringBuilder postString () {
        return new PostStringBuilder();
    }

    public static PostFileBuilder postFile () {
        return new PostFileBuilder();
    }

    public static PostFormBuilder post () {
        return new PostFormBuilder();
    }

    public static OtherRequestBuilder put () {
        return new OtherRequestBuilder(METHOD.PUT);
    }

    public static HeadBuilder head () {
        return new HeadBuilder();
    }

    public static OtherRequestBuilder delete () {
        return new OtherRequestBuilder(METHOD.DELETE);
    }

    public static OtherRequestBuilder patch () {
        return new OtherRequestBuilder(METHOD.PATCH);
    }

    public void execute (final RequestCall requestCall, Callback callback) {

        if (callback == null)
            callback = Callback.CALLBACK_DEFAULT;
        final Callback finalCallback = callback;
        final int id = requestCall.getOkHttpRequest().getId();

        requestCall.getCall().enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure (Call call, final IOException e) {
                sendFailResultCallback(call, e, finalCallback, id);
            }

            @Override
            public void onResponse (final Call call, final Response response) {
                try {
                    if (mContext != null && HttpNetworkUtils.isNetworkAvailable(mContext)) {
                        sendFailResultCallback(call, new HttpBaseResponseUtils.NetWorkTipException("Network is unreachable"), finalCallback, id);
                        return;
                    }
                    if (call.isCanceled()) {
                        sendFailResultCallback(call, new IOException("Canceled!"), finalCallback, id);
                        return;
                    }

                    if (!finalCallback.validateReponse(response, id)) {
                        sendFailResultCallback(call, new IOException("request failed , reponse's code is : " + response.code()), finalCallback, id);
                        return;
                    }
                    Object ob = null;
                    try {
                        ob = finalCallback.parseNetworkResponse(response, id);
                    } catch (com.alibaba.fastjson.JSONException e) {
                        e.printStackTrace();
                        sendFailResultCallback(call, e, finalCallback, id);
                        return;
                    }
                    if (ob instanceof AbsBaseReponse) {
                        AbsBaseReponse mAbsBaseReponse = (AbsBaseReponse) ob;
                        int status = mAbsBaseReponse.getHeader().getStatus();
                        switch (status) {
                            case HttpBaseResponseUtils.HttpResponseCode.HTTP_SUCCESS: // 1 正常
                                sendSuccessResultCallback(ob, finalCallback, id);
                                break;
                            case HttpBaseResponseUtils.HttpResponseCode.HTTP_INVALIDATE_PARAMETERS: // 2 参数异常	参数错误
                                sendFailResultCallback(call, new HttpBaseResponseUtils.TipException(mAbsBaseReponse.getHeader().getMessage(), status),
                                        finalCallback,
                                        id);
                                break;
                            case HttpBaseResponseUtils.HttpResponseCode.HTTP_SERVER_ERROR: // 3 系统异常	后台处理异常，不显示给用户
                                sendFailResultCallback(call, new HttpBaseResponseUtils.NoneTipException("Server System Error!"), finalCallback, id);
                                break;
                            case HttpBaseResponseUtils.HttpResponseCode.HTTP_BUSINESS_ERROR: // 4 业务异常	业务逻辑异常，可直接显示给用户
                                sendFailResultCallback(call, new HttpBaseResponseUtils.TipException(mAbsBaseReponse.getHeader().getMessage(),
                                        status), finalCallback, id);
                                break;
                            case HttpBaseResponseUtils.HttpResponseCode.HTTP_TOKEN_ERROR: // 5 token异常	超时，强制登出重新生成
                                String exceptionMsg = mAbsBaseReponse.getHeader().getMessage();
                                sendFailResultCallback(call, new HttpBaseResponseUtils.TokenInvalidException(exceptionMsg), finalCallback, id);
                                break;
                            case HttpBaseResponseUtils.HttpResponseCode.HTTP_SPECIAL_SERVER_ERROR: // 6 其他需要特殊处理的业务异常
                                sendFailResultCallback(call, new HttpBaseResponseUtils.TipException(mAbsBaseReponse.getHeader().getMessage(), status),
                                        finalCallback, id);
                                break;
                        }
                    }

                } catch (Exception e) {
                    sendFailResultCallback(call, e, finalCallback, id);
                } finally {
                    if (response.body() != null)
                        response.body().close();
                }

            }
        });
    }

    public void sendFailResultCallback (final Call call, final Exception e, final Callback callback, final int id) {
        if (callback == null) return;

        mPlatform.execute(new Runnable() {
            @Override
            public void run () {
                callback.onError(call, e, id);
                callback.onAfter(id);
            }
        });
    }

    public void sendSuccessResultCallback (final Object object, final Callback callback, final int id) {
        if (callback == null) return;
        mPlatform.execute(new Runnable() {
            @Override
            public void run () {
                callback.onResponse(object, id);
                callback.onAfter(id);
            }
        });
    }

    public void cancelTag (Object tag) {
        for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
        for (Call call : mOkHttpClient.dispatcher().runningCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
    }

    public void cancelAllRequest () {
        for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
            call.cancel();
        }
        for (Call call : mOkHttpClient.dispatcher().runningCalls()) {
            call.cancel();
        }
    }

    public static class METHOD {
        public static final String HEAD = "HEAD";
        public static final String DELETE = "DELETE";
        public static final String PUT = "PUT";
        public static final String PATCH = "PATCH";
    }
}

